package com.qianfeng.log;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qianfeng.exception.MyBaseException;
import com.qianfeng.log.annotations.LogAnnotation;
import com.qianfeng.pojo.BaseUser;
import com.qianfeng.pojo.SysOperLog;
import com.qianfeng.pojo.SysUserInfo;
import com.qianfeng.service.OperLogService;
import com.qianfeng.utils.LoginUsersUtils;
import com.qianfeng.utils.RequestUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/*
 *@Description:
 *@author:刘哥
 *@Date:86180 2022/9/15 17:07
 */
@Aspect//配置切面
@Component
public class LogAdvice {
    private OperLogService operLogService;

    @Autowired
    public void setOperLogService(OperLogService operLogService) {
        this.operLogService = operLogService;
    }

    private ObjectMapper objectMapper;
    @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Pointcut("@annotation(com.qianfeng.log.annotations.LogAnnotation)")
    public void puintcut(){}

    @AfterReturning(pointcut="puintcut()",returning = "result")
    public void after(JoinPoint joinPoint,Object result){
        SysOperLog operLog=new SysOperLog();
        try {
            operLog.setJsonResult(objectMapper.writeValueAsString(result));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        operLog.setStatus(1);
        operLog.setErrorMsg("200 OK");
        addLog(operLog,joinPoint);
    }



    @AfterThrowing(pointcut="puintcut()",throwing = "exp")
    public void afterThrowing(JoinPoint joinPoint,Exception exp){
        SysOperLog operLog = new SysOperLog();
        operLog.setStatus(0);
        if (exp instanceof MyBaseException){
            operLog.setErrorMsg(((MyBaseException) exp).getCode()+","+exp.getMessage());
        }else {
            operLog.setErrorMsg(exp.getMessage());
        }
        addLog(operLog,joinPoint);
    }

    private void addLog(SysOperLog operLog, JoinPoint joinPoint) {
//        通过代理对象获取对象方法
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
//        获取方法上面的注解
        LogAnnotation annotation = method.getAnnotation(LogAnnotation.class);
        String title = annotation.title();//获取注解里面的title属性值(指定的标题)
        int business_type = annotation.business_type();//获取注解里面代表操作类型的值

//        获取请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //请求相关的数据可以从request中获取,比如地址, 请求方式,ip,访问的客户端类型,
        //传递的参数其实就是方法的参数,我们可以将方法的参数获取到,然后转成数据保存过去,但是我们的参数可能会有多个,但是在表中我们只有一个列,怎么用一个列来保存所有的值?
        //我们可以将这些参数转成json,放到一个map中转成json
        //获取数据,保存到数据库
        System.err.println(title + "=----->" + business_type);
//        获取ip地址
        String ip = RequestUtil.getRemoteHost(request);
        String locationByIp = RequestUtil.getLocationByIp(ip);
//        获取登录对象
        BaseUser user = LoginUsersUtils.getLoginUser();
//        获取请求路径
        String requestURI = request.getRequestURI();
//        获取请求里面的浏览器信息和系统信息
        Map<String, String> osAndBrowserInfo = RequestUtil.getOsAndBrowserInfo(request);
//        获取方法名
        String methodName = method.getName();
        //方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("args = " + args.toString());
//       获取参数名数组
        Parameter[] parameters = method.getParameters();
        System.out.println("parameters = " + parameters.toString());
        HashMap<Object, Object> parameterMap = new HashMap<>();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            Object value = args[i];
            parameterMap.put(parameter.getName(),value);
        }

        operLog.setTitle(title);
        operLog.setBusinessType(business_type);
        operLog.setMethod(methodName);
        operLog.setRequestMethod(request.getMethod());
        operLog.setOperTime(new Date());
        //根据访问的设备来区分类型
        String os = osAndBrowserInfo.get("os");

        switch (os){
            case "Android":
            case "IPhone":
                operLog.setOperatorType(2);
                break;
            case "Windows":
            case "Mac":
            case "Unix":
                operLog.setOperatorType(1);
                break;
            default:
                operLog.setOperatorType(0);
                break;
        }
//         封装日志对象
        operLog.setOperName(user.getUsername());
        operLog.setOperUrl(requestURI);
        operLog.setOperIp(ip);
        operLog.setOperLocation(locationByIp);

        try {
//        把参数的map变为json格式存到参数中
            operLog.setOperParam(objectMapper.writeValueAsString(parameterMap));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        System.out.println("operLog = " + operLog);
        operLogService.addoperLog(operLog);
    }

}
